#include<stdio.h>
#include<event.h>
#include<string.h>//bzero
#include<netinet/in.h>//struct sockaddr_in 

#include"socket_wrap.h"

#define SRV_PORT 10087
#define MAX_CLIENT 1024

// 1.创建套接字
// 2.绑定
// 3.监听
// 4.创建event_base根节点
// 5.初始化上树节点 lfd
// 6.上树
// 7.循环监听
// 8.收尾

//解决ev指针改变问题：需要数组存储多个结构体
typedef struct fd_event_map{
    int fd;
    struct event *ev;
}fd_event_map;
fd_event_map array[MAX_CLIENT];

void event_array_init()
{
    int i;
    for(i=0;i<MAX_CLIENT;i++){
        array[i].fd=-1;
        array[i].ev=NULL;
    }

}

void event_array_free()
{   
    int i;

    for(i=0;i<MAX_CLIENT;i++){
        if(array[i].fd>0&&array[i].ev){
            close(array[i].fd);
            array[i].fd=-1;
            event_free(array[i].ev);
        }
    }
}

//把ev指针保存到数组中
int add_eventptr(int fd,struct event *ev)
{
    int i;
    for(i=0;i<MAX_CLIENT;i++){
        if(array[i].fd<0){//???
            break;
        }
    }

    if(i==MAX_CLIENT){
        printf("too manny clients...");
        return -1;
    }

    array[i].fd=fd;
    array[i].ev=ev;
    return 0;
}

//根据fd获取对应event
struct evnet* get_event_by_fd(int fd)
{ 
    int i;
    for(i=0;i<MAX_CLIENT;i++){
        if(array[i].fd==fd){
            return array[i].ev;
        }
    }
}

//cfd在树上被触发事件
void cfd_cb(int cfd,short event,void *arg)
{
    char buf[1024]="";

    int n=Read(cfd,buf,sizeof(buf));
    if(n<=-1){
        perr_exit("lfd_cb");
        event_del(get_event_by_fd(cfd));//下树:通过cfd查询数组里的event结构体
    }else{  
        printf("%s\n",buf);
        write(cfd,buf,n);
    }
}

//等于说就是libevent代替了自己写的while(1)监听循环，有事情直接通知lfd监听回调函数
void lfd_cb(int lfd,short event,void *arg)
{
    //还需要再用一下基座base
    struct event_base *base=(struct event_base *)arg;

    //a：等于说这里不关心cfd的结构只关心文件描述符，所以参2和参3都是null？？？
    int cfd=Accept(lfd,NULL,NULL);

    //创建节点cfd准备上树
    //ev问题：每次连接到来调用回调函数ev指针的值会改变，在下数的时候就找不到这个ev结构体了
    struct event *ev=event_new(base,cfd,EV_READ|EV_PERSIST,cfd_cb,NULL);

    //保存这个指针
    add_eventptr(cfd,ev);

    //将cfd上树
    event_add(ev,NULL);
}

int main(int argc, char** argv)
{
	int lfd;//门卫套接字的文件描述符
    struct sockaddr_in serv_addr;

	//s
	lfd=Socket(PF_INET, SOCK_STREAM, 0);

	//b:设置地址族：地址族为ipv4、ip号、端口号
	//memset(&serv_addr, 0, sizeof(serv_addr));
	bzero(&serv_addr,sizeof(serv_addr));//置零，我的评价是比memset()好用
	serv_addr.sin_family=PF_INET;//ipv4
	serv_addr.sin_port=htons(SRV_PORT);//port:如果port用宏则不用atoi()
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//ip
	Bind(lfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr));

	//l:它不是等待监听，是设置监听上限（同时建立三次握手的客户端数量）
	Listen(lfd, 128);

    //插入:初始化数组
    event_array_init();

    //创建event_base根节点
    struct event_base *base=event_base_new();

    //初始化一个上树节点lfd:开启读事件并持续监听
    //参数4：callback，回调函数
    //参数5：callback_arg,回调函数的参数
    struct event *ev=event_new(base,lfd,EV_READ | EV_PERSIST,lfd_cb,base);

    //lfd节点上树
    event_add(ev,NULL);

    //循环监听
    event_base_dispatch(base);//阻塞

    //收尾
    event_array_free();//销毁数组
    close(lfd);
    event_base_free(base);
    return 0;
}
